#include "udp.h"
#include "helper.h"
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

UDPServer::UDPServer( const std::string port, const std::string listenAddress, const bool ipv6 ):bound_(false)
{
	memset( (char *) &listenAddress_, 0, sizeof(listenAddress_)  ); //initialize the listenAddress
#ifdef SIN6_LEN	
	listenAddress_.sin6_len = sizeof( listenAddress_ );
#endif
	listenAddress_.sin6_port = htons( ss_atoi< uint16_t >( port ) );
	if( listenAddress == "any" )
	{
		listenAddress_.sin6_addr = in6addr_any;
	}
	else
	{
		int result = inet_pton( AF_INET6, listenAddress.c_str(), &listenAddress_.sin6_addr );
		if( result < 1 )
		{
			if( result == 0 )
			{
				std::cerr << "ERROR: Bad address " << listenAddress << std::endl;
			}
			else
			{
				throw UDPError( "inet_pton failure" );
			}
		}
	}
	
	listenAddress_.sin6_family = AF_INET6;
	createSocket();
}

UDPMessage UDPServer::incommingMessage()
{
	uint8_t buffer[MAX_UDP_DNS_PACKET_SIZE];
	if( !bound_ )
	{
		if ( bind(socket_, (struct sockaddr *)&listenAddress_, sizeof(listenAddress_)) < 0 )
		{
			//error?
			std::cerr << "ERROR binding socket" << std::endl;
			printError( );
			exit(2);
		}
		bound_ = true;
	}

	fd_set sockets;
	FD_ZERO( &sockets ); //initialization
	FD_SET( socket_, &sockets );
	if( select( socket_ + 1, &sockets, NULL, NULL, NULL) < 0 )
	{
		printError();
		throw UDPError( "Error with sleect" );
	}

	struct sockaddr_in6 fromAddr;
	socklen_t fromLen = sizeof( fromAddr );
	//fromAddr.sin6_family = AF_INET6;
		//error
  int size = recvfrom( socket_, &buffer, MAX_UDP_DNS_PACKET_SIZE, 0, (sockaddr*) &fromAddr, &fromLen);
	if( size < 0 )
	{
		printError();
		throw UDPError( "Error receiving Data" );
	}

	UDPMessage message;
	message.setSource( fromAddr );
	message.setData( buffer, size );
	message.setDestination( listenAddress_ );
	return message;
}

void UDPServer::printError() const
{
	switch( errno )
	{
		case EACCES:
			std::cerr << "The requested address is protected, and the current user has inadequet premission to access it" << std::endl;
			break;
		case EADDRINUSE:
			std::cerr << "The specified address is already in use" << std::endl;
			break;
		case EADDRNOTAVAIL: 
			std::cerr << "The specified address is not available on the local machine" << std::endl;
			break;
		case EAFNOSUPPORT:
			std::cerr << "Address is not valid for the address family of socket" << std::endl; 
			break;
		case EBADF:
			std::cerr << "Socket is not a vailid file descriptor" << std::endl;
			break;
		case EDESTADDRREQ:
			std::cerr << "Socket is a null pointer" << std::endl;
			break;
		case EFAULT:
			std::cerr << "The address parameter is not in the valid part of the user address space" << std::endl;
			break;
		case EINVAL:
			std::cerr << "socket is already bound to an address and the protocol does not support binding to a new address.  Alternatively, socket may have been shut down." << std::endl;
			break;
		case ENOTSOCK:
			std::cerr << "socket does not refer to a socket." << std::endl;
			break;
		case EOPNOTSUPP: 
			std::cerr << "socket is not of a type that can be bound to an address." << std::endl;
			break;
		case EIO:
			std::cerr << "An I/O error occurred while making the directory entry or allocating the inode." << std::endl;
			break;
		case EISDIR:
			std::cerr << "An empty pathname was specified." << std::endl;
			break;
		case ELOOP:
			std::cerr << "Too many symbolic links were encountered in translating the pathname.  This is taken to be indicative of a looping symbolic link." << std::endl;
			break;
		case ENAMETOOLONG:
			std::cerr << "A component of a pathname exceeded {NAME_MAX} characters, or an entire path name exceeded {PATH_MAX} characters." << std::endl;
			break;
		case ENOENT:
			std::cerr << "A component of the path name does not refer to an existing file." << std::endl;
			break;
		case ENOTDIR:
			std::cerr << "A component of the path prefix is not a directory." << std::endl;
			break;
		case EROFS:
			std::cerr << "The name would reside on a read-only file system." << std::endl;
			break;
		default:
			std::cerr << "UNKNOWN ERROR: " << errno << std::endl;
	}

}

UDPServer::~UDPServer()
{

}

void UDPServer::setPortNum( const uint16_t &portnum )
{
	listenAddress_.sin6_port = htons( portnum );
	createSocket();
	bound_ = false;
}

void UDPServer::setPortNum( const std::string &portnum )
{
	listenAddress_.sin6_port = htons( ss_atoi<uint16_t>( portnum ) );
	createSocket();
	bound_ = false;
}

void UDPServer::setListenAddress( const std::string &listenAddress )
{
	int result = inet_pton( AF_INET6, listenAddress.c_str(), &listenAddress_.sin6_addr );
	if( result < 1 )
	{
		if( result == 0 )
		{
			std::cerr << "ERROR: Bad address " << listenAddress << std::endl;
		}
		else
		{
			throw UDPError( "inet_pton failure" );
		}
	}

	createSocket();
	bound_ = false;
}

void UDPServer::createSocket() 
{
	if( socket_ )
		close( socket_ );

	socket_ = socket( AF_INET6, SOCK_DGRAM, 0 );
	if( socket_  < 0 )
	{
		//error
		throw UDPError( "Failed to create socket" );
	}
}
